/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "SceneCamera.h"

namespace PQBuilder
{
	const float SceneCamera::CAMERA_EPSILON=0.01f;
	SceneCamera::SceneCamera(PQEngine::SceneObject* object)
		: _sceneObject(object), _rotateMode(ROTATE_CAMERA_PIVOT),_needUpdate(false)
	{
		assert(_sceneObject);
		
		_camera=_sceneObject->getCamera();
		_camera->setPosition(0.0f,20.0f,100.0f);
		_camera->lookAt(0.0f,0.0f,0.0f);

		_position=_camera->getDerivedPosition();
	}

	void SceneCamera::rotate(int x0,int y0,int x1,int y1)
	{
		float deltaX = (x1 - x0) * 0.5f;
		float deltaY = (y1 - y0) * 0.5f;
		
		_camera->yaw(Ogre::Degree(-deltaX / 4.0f));
        _camera->pitch(Ogre::Degree(-deltaY / 4.0f));
	}

	void SceneCamera::rotateObject(int x0,int y0,int x1,int y1)
	{
		float deltaX = (x1 - x0) * 0.5f;
		float deltaY = (y1 - y0) * 0.5f;
		PQEngine::GameObject* obj=_sceneObject->getGameObject();
		if(!obj){
			_camera->yaw(Ogre::Degree(-deltaX / 4.0f));
			_camera->pitch(Ogre::Degree(-deltaY / 4.0f));
			return;
		}
		
		//There is problem when look just on top of the selected game object
		Ogre::Vector3 lookat=obj->getDerivedPosition();
		Ogre::Vector3 distance=_camera->getPosition()-lookat;
		float length=distance.length();
		distance+=(_camera->getRight()*-deltaX*length/48.0f)+(_camera->getUp()*deltaY*length/96.0f);
		distance.normalise();
		Ogre::Vector3 pos=lookat+distance*length;
		_camera->setPosition(pos);
		_camera->lookAt(lookat);


	}


	void SceneCamera::pan(int x0,int y0,int x1,int y1)
	{
		float deltaX = (x1 - x0) * 0.5f;
		float deltaY = (y1 - y0) * 0.5f;

		Ogre::Vector3 pos=_camera->getPosition();
		Ogre::Vector3 delta=Ogre::Vector3(deltaX*PAN_SPEED/3.0f,-deltaY*PAN_SPEED/3.0f,0);
		_camera->setPosition(pos+_camera->getOrientation()*delta);
	}

	void SceneCamera::scale(int x0,int y0,int x1,int y1)
	{
		float deltaX = (x1 - x0) * 0.5f;
		float deltaY = (y1 - y0) * 0.5f;

		
	}

	void SceneCamera::frameObject(PQEngine::GameObject* obj)
	{
		if(obj==NULL||obj->isTerrain()){
			return;
		}
		
		Ogre::AxisAlignedBox bbox=obj->getWorldBoundingBox();
		if(bbox==Ogre::AxisAlignedBox::BOX_NULL){
			return;
		}

		Ogre::Vector3 size=bbox.getHalfSize();
		Ogre::Vector3 center=bbox.getCenter();
		size+=Ogre::Vector3(size.z,size.z,size.z);

		float distance=std::max(std::max(size.x,size.y),size.z) * 2.0f;
		Ogre::Vector3 lookat=_camera->getDirection()*distance;

		_position=center-lookat;
		_needUpdate=true;
	}

	void SceneCamera::frameObject(int id)
	{
		PQEngine::Object* obj=PQEngine::Object::getObject(id);
		if(PQEngine::Object::isGameObject(obj)){
			frameObject((PQEngine::GameObject*)obj);
		}
	}


	void SceneCamera::frameObject()
	{
		PQEngine::GameObject* obj=_sceneObject->getGameObject();
		frameObject(obj);
		
	}

	void SceneCamera::update(float deltaT)
	{
		if(!_needUpdate){
			return;
		}

		Ogre::Vector3 pos=_camera->getPosition();
		Ogre::Vector3 distance=_position-pos;
		if(distance.length()>CAMERA_EPSILON){
			pos+=distance*deltaT*10;//1 second
			_camera->setPosition(pos);
		}else{
			_camera->setPosition(_position);
			_needUpdate=false;
		}

	}

	void SceneCamera::forward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getDirection().normalisedCopy()*CAMERA_SPEED;
		_position+=delta;
		_camera->setPosition(_position);
	}

	void SceneCamera::backward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getDirection().normalisedCopy()*CAMERA_SPEED;
		_position-=delta;
		_camera->setPosition(_position);
	}

	void SceneCamera::leftward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getRight().normalisedCopy()*CAMERA_SPEED;
		_position-=delta;
		_camera->setPosition(_position);
	}

	void SceneCamera::rightward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getRight().normalisedCopy()*CAMERA_SPEED;
		_position+=delta;
		_camera->setPosition(_position);
	}

	void SceneCamera::upward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getUp().normalisedCopy()*CAMERA_SPEED;
		_position+=delta;
		_camera->setPosition(_position);
	}

	void SceneCamera::downward()
	{
		_position=_camera->getDerivedPosition();
		Ogre::Vector3 delta=_camera->getUp().normalisedCopy()*CAMERA_SPEED;
		_position-=delta;
		_camera->setPosition(_position);
	}


}
